﻿namespace TetrisWindows.ModelTetris

open System
open System.Diagnostics

    [<Class>]
    type public state_shape=class
        static member mPieces=[| 
                                // Square
                                 [| [| [|0; 0; 0; 0; 0|] ; [|0; 0; 0; 0; 0|] ; [|0; 0; 2; 1; 0|] ; [|0; 0; 1; 1; 0|] ;   [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|] ; [|0; 0; 0; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 1; 1; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 1; 1; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 1; 1; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            // I
                                 [| [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 1; 2; 1; 1|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 0; 2; 0; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 1; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|1; 1; 2; 1; 0|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 1; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 0; 2; 0; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            // L
                                 [| [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 0; 2; 0; 0|];  [|0; 0; 1; 1; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 1; 2; 1; 0|];  [|0; 1; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 1; 1; 0; 0|];  [|0; 0; 2; 0; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 1; 0|];  [|0; 1; 2; 1; 0|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            // L mirrored
                                 [| [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 0; 2; 0; 0|];  [|0; 1; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 1; 0; 0; 0|];  [|0; 1; 2; 1; 0|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 1; 0|];  [|0; 0; 2; 0; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 1; 2; 1; 0|];  [|0; 0; 0; 1; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            // N
                                 [| [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 1; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 1; 2; 0; 0|];  [|0; 0; 1; 1; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 1; 2; 0; 0|];  [|0; 1; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 1; 1; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            // N mirrored
                                 [| [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 0; 1; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 1; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 1; 0; 0; 0|];  [|0; 1; 2; 0; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 1; 0|];  [|0; 1; 2; 0; 0|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            // T
                                 [| [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 0; 2; 1; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |] 
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 0; 0; 0|];  [|0; 1; 2; 1; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 1; 2; 0; 0|];  [|0; 0; 1; 0; 0|];    [|0; 0; 0; 0; 0|]  |]
                                    [| [|0; 0; 0; 0; 0|];  [|0; 0; 1; 0; 0|];  [|0; 1; 2; 1; 0|];  [|0; 0; 0; 0; 0|];    [|0; 0; 0; 0; 0|]  |] |]
                            |]

        static member mPiecesInitialPosition=[|
                                            (* Square *)
                                              [| [|-2; -3|]; [|-2; -3|];  [|-2; -3|];  [|-2; -3|] |]
                                            ///* I */
                                              [| [|-2; -2|]; [|-2; -3|];  [|-2; -2|];  [|-2; -3|] |]
                                            ///* L */
                                              [| [|-2; -3|]; [|-2; -3|];  [|-2; -3|];  [|-2; -2|] |]
                                            ///* L mirrored */
                                              [| [|-2; -3|]; [|-2; -2|];  [|-2; -3|];  [|-2; -3|] |]
                                            //* N */
                                              [| [|-2; -3|]; [|-2; -3|];  [|-2; -3|];  [|-2; -2|] |]
                                            //* N mirrored */
                                              [| [|-2; -3|]; [|-2; -3|];  [|-2; -3|];  [|-2; -2|] |]
                                            //* T */
                                              [| [|-2; -3|]; [|-2; -3|];  [|-2; -3|];  [|-2; -2|] |]
                                            |]
    end

    [<Class>]
    type public Pieces=class
        public new() ={}

        member public this.GetBlockType(pPiece:int, pRotation:int, pX:int, pY:int)=
             Array.get (Array.get (Array.get (Array.get state_shape.mPieces (pPiece)) pRotation) pY) pX
        
        member public this.GetXInitialPosition(pPiece:int, pRotation:int)=
            Array.get (Array.get (Array.get state_shape.mPiecesInitialPosition pPiece) pRotation) 0

        member public this.GetYInitialPosition(pPiece:int, pRotation:int)=
            Array.get (Array.get (Array.get state_shape.mPiecesInitialPosition pPiece) pRotation) 1

    end

    [<Flags>]
    type public State_Model=
        | FREE
        | FILLED

    [<Class>]
    type public Board public ()=class
        
        [<DefaultValue>]
        static val mutable private BOARD_LINE_WIDTH:int
        [<DefaultValue>]
        static val mutable private BLOCK_SIZE:int
        [<DefaultValue>]
        static val mutable private BOARD_POSITION:int
        [<DefaultValue>]
        static val mutable private BOARD_WIDTH:int
        [<DefaultValue>] 
        static val mutable private BOARD_HEIGHT:int
        [<DefaultValue>] 
        static val mutable private MIN_VERTICAL_MARGIN:int
        [<DefaultValue>]
        static val mutable private MIN_HORIZONTAL_MARGIN:int
        [<DefaultValue>]
        static val mutable private PIECE_BLOCKS:int

        static member public board_line_width
            with get() = Board.BOARD_LINE_WIDTH
            and set(value) = Board.BOARD_LINE_WIDTH<-value
        
        static member public block_size
            with get() = Board.BLOCK_SIZE
            and set(value) = Board.BLOCK_SIZE<-value
        
        static member public board_position
            with get() = Board.BOARD_POSITION
            and set(value) = Board.BOARD_POSITION<-value
        
        static member public board_width
            with get() = Board.BOARD_WIDTH
            and set(value) = Board.BOARD_WIDTH<-value
        
        static member public board_height
            with get() = Board.BOARD_HEIGHT
            and set(value) = Board.BOARD_HEIGHT<-value
        
        static member public min_vertical_margin
            with get() = Board.MIN_VERTICAL_MARGIN
            and set(value) = Board.MIN_VERTICAL_MARGIN<-value
            
        static member public min_horizontal_margin
            with get() = Board.MIN_HORIZONTAL_MARGIN
            and set(value) = Board.MIN_HORIZONTAL_MARGIN<-value
         
        static member public piece_blocks
            with get() = Board.PIECE_BLOCKS
            and set(value) = Board.PIECE_BLOCKS<-value
        
        
        
        static member public FillData()=
            Board.BOARD_LINE_WIDTH<-6
            Board.BLOCK_SIZE<-16
            Board.BOARD_POSITION<-320
            Board.BOARD_WIDTH<-10
            Board.BOARD_HEIGHT<-20
            Board.MIN_VERTICAL_MARGIN<-20
            Board.MIN_HORIZONTAL_MARGIN<-20
            Board.PIECE_BLOCKS<-5

        static do
            Board.BOARD_LINE_WIDTH<-6
            Board.BLOCK_SIZE<-16
            Board.BOARD_POSITION<-320
            Board.BOARD_WIDTH<-10
            Board.BOARD_HEIGHT<-20
            Board.MIN_VERTICAL_MARGIN<-20
            Board.MIN_HORIZONTAL_MARGIN<-20
            Board.PIECE_BLOCKS<-5

        [<DefaultValue>]
        val mutable public mScreenHieght:int
        [<DefaultValue>]
        val mutable public mBoard:array<array<State_Model>>
        [<DefaultValue>]
        val mutable public mPieces:Pieces

        member public this.ClearBoard()=
            this.mBoard<-Array.init (Board.board_width) (fun i -> Array.init Board.board_height (fun j -> State_Model.FREE))
            
        member public this.Init(pPieces:Pieces, pScreenHeight:int)=
            this.mScreenHieght<-pScreenHeight
            this.mPieces<-pPieces
            this.ClearBoard()
            ()
        
        member public this.StorePiece(pX:int, pY:int, pPiece:int, pRotation:int)=
            let mutable i2=0
            for i1=pX to pX+Board.piece_blocks-1 do
                let mutable j2=0
                for j1=pY to pY+Board.piece_blocks-1 do
                    if (this.mPieces.GetBlockType(pPiece, pRotation, j2, i2) <> 0) then
                         Array.set (Array.get this.mBoard i1) j1 State_Model.FILLED
                    j2<-j2+1
                i2<-i2+1
            ()
            
        
        member public this.IsGameOver()=
            let rec loop(i:int)=
                if (i<Board.board_width) then
                    let b=(Array.get this.mBoard i)
                    if ((Array.get b 0)=State_Model.FILLED) then
                        true
                    else
                        loop(i+1)
                else
                    false
            loop(0)         

        member public this.DeleteLine(pY:int)=
            for j=pY downto 1 do
               for i = 0 to Board.board_width-1 do
                    let a=Array.get this.mBoard i
                    Array.set a j (Array.get a (j-1))
        
        member public this.DeletePossibleLines()=
            for j=0 to Board.board_height-1 do
                let rec loop(i:int ref)=
                    if ((!i)<Board.board_width) then
                        let a=Array.get (Array.get this.mBoard !i) j
                        i:=(!i)+1
                        if (a=State_Model.FILLED) then loop(i) else ()   
                let i:int ref=ref 0       
                loop(i)
                if ((!i)=Board.board_width) then
                        this.DeleteLine(j)

        member public this.IsFreeBlock(pX:int, pY:int)=
            let a=Array.get (Array.get this.mBoard pX) pY
            (a=State_Model.FREE)

        member public this.GetXPosInPixels(pPos:int)=
            ( ( Board.BOARD_POSITION - (Board.BLOCK_SIZE * (Board.BOARD_WIDTH / 2)) ) + (pPos * Board.BLOCK_SIZE) )
        member public this.GetYPosInPixels(pPos:int)=
            ( (this.mScreenHieght - (Board.BLOCK_SIZE * Board.BOARD_HEIGHT)) + (pPos * Board.BLOCK_SIZE) )

        member public this.IsPossibleMovement(pX:int, pY:int, pPiece:int, pRotation:int)=
            let ans:bool ref=ref true

            let rec loop(i1:int, i2:int)=
                if (i1<pX+Board.PIECE_BLOCKS) then
                    let b:bool ref=ref true
                    
                    let rec loop2(i1:int, i2: int, j1:int, j2:int) =
                        if (j1<pY+Board.PIECE_BLOCKS) then
                            let mutable b1=true
                            if (i1<0 || i1> Board.BOARD_WIDTH-1 || j1>Board.BOARD_HEIGHT-1) then
                                if (this.mPieces.GetBlockType(pPiece, pRotation, j2, i2)<>0) then
                                    b1<-false
                                    b:=false
                                    ans:=false
                            if (j1>=0 && (b1)) then
                                if (this.mPieces.GetBlockType(pPiece, pRotation, j2, i2)<>0 && not(this.IsFreeBlock(i1, j1))) then
                                    b1<-false
                                    b:=false
                                    ans:=false
                            if (b1) then
                                loop2(i1, i2, j1+1, j2+1)
                    loop2(i1, i2, pY, 0)
                    if (!b) then
                        loop(i1+1, i2+1)
            loop(pX, 0)
            (!ans)
        
    end        

    [<Class>]
    type public ModelTetris =class
        inherit Board
        
        [<DefaultValue>]
        val mutable public mPosX:int
        [<DefaultValue>]
        val mutable public mPosY:int
        [<DefaultValue>]
        val mutable public mPeice:int
        [<DefaultValue>]
        val mutable public mRotation:int
       // [<DefaultValue>]
        val mutable private mScreenHeight:int
        [<DefaultValue>]
        val mutable public mNextPosX:int
        [<DefaultValue>]
        val mutable public mNextPosY:int
        [<DefaultValue>]
        val mutable public mNextPiece:int    
        [<DefaultValue>]
        val mutable public mNextRotation:int    

        val mutable public mPieces:Pieces
        
        val mutable private rnd:Random

        public new(ScreenHeight:int)=
            {
                mScreenHeight=ScreenHeight;
                mPieces=new Pieces();
                rnd=new Random();
            }
        
        member public this.ScreenHeight
            with get() = this.mScreenHeight
            and set(value) = this.mScreenHeight <-value


        member public this.InitGame()=
            this.Init(this.mPieces, this.mScreenHeight);

            this.mPeice<-this.rnd.Next(0, 6)
            this.mRotation<-this.rnd.Next(0,3)
            this.mPosX<-Board.board_width/2+this.mPieces.GetXInitialPosition(this.mPeice, this.mRotation)
            this.mPosY<-this.mPieces.GetYInitialPosition(this.mPeice, this.mRotation)
            this.mNextPiece<-this.rnd.Next(0,6)
            this.mNextRotation<-this.rnd.Next(0,3)
            this.mNextPosX<-Board.board_width+5
            this.mNextPosY<-5;
        
        member public this.CreateNewPiece()=
            this.mPeice<-this.mNextPiece
            this.mRotation<-this.mNextRotation
            this.mPosX<-Board.board_width/2+this.mPieces.GetXInitialPosition(this.mPeice, this.mRotation)
            this.mPosY<-this.mPieces.GetYInitialPosition(this.mPeice, this.mRotation)
            this.mNextPiece<-this.rnd.Next(0,6)
            this.mNextRotation<-this.rnd.Next(0,3)

        member public this.GetBlockTypeFrom(pPiece:int, pRotation:int, pX:int, pY:int)=
            this.mPieces.GetBlockType(pPiece, pRotation, pX, pY);

    end     

                     
    

     
                                
        
    

